package org.infodavid.common.persistence.impl;

import java.io.InputStream;

import javax.persistence.EntityManagerFactory;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.event.CacheEventListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.cache.AbstractCache;
import org.infodavid.common.persistence.IConnectionConfiguration;

/**
 * The Class EntityManagerFactoryCache.
 */
public final class EntityManagerFactoryCache extends
    AbstractCache<EntityManagerFactory,IConnectionConfiguration> {

	/** The Constant CONFIG_FILE. */
	public static final String CONFIG_FILE = EntityManagerFactoryCache.class.getSimpleName() + ".xml";

	/** The Constant CACHE_NAME. */
	private static final String CACHE_NAME = EntityManagerFactoryCache.class.getSimpleName();

	/** The logger. */
	private static final Log LOGGER = LogFactory.getLog(EntityManagerFactoryCache.class);

	/**
	 * The Class CleanupCacheEventListener.
	 */
	private static class CleanupCacheEventListener implements CacheEventListener {
		/** The logger. */
		private static final Log LOG = LogFactory.getLog(CleanupCacheEventListener.class);

		/**
		 * The Constructor.
		 */
		public CleanupCacheEventListener() {
			super();
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Object#clone()
		 */
		public Object clone() throws CloneNotSupportedException {
			return super.clone();
		}

		/**
		 * Close.
		 * @param elt the elt
		 */
		private void close(final Element elt) {
			final Object value = elt.getObjectValue();

			if (value instanceof EntityManagerFactory) {
				try {
					((EntityManagerFactory)value).close();
				}
				catch (final Exception e) {
					LOG.warn("An error occurs while closing factory associated to key: " + elt.getKey());
				}
			}
		}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyRemoveAll(net.sf.ehcache.Ehcache)
		 */
		public void notifyRemoveAll(final Ehcache cache) {}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyElementUpdated(net.sf.ehcache.Ehcache,
		 * net.sf.ehcache.Element)
		 */
		public void notifyElementUpdated(final Ehcache cache, final Element elt) {}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyElementRemoved(net.sf.ehcache.Ehcache,
		 * net.sf.ehcache.Element)
		 */
		public void notifyElementRemoved(final Ehcache cache, final Element elt) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Closing factory associated to key (removal): " + elt.getKey());
			}

			close(elt);
		}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,
		 * net.sf.ehcache.Element)
		 */
		public void notifyElementPut(final Ehcache cache, final Element elt) {}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyElementExpired(net.sf.ehcache.Ehcache,
		 * net.sf.ehcache.Element)
		 */
		public void notifyElementExpired(final Ehcache cache, final Element elt) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Closing factory associated to key (expiration): " + elt.getKey());
			}

			close(elt);
		}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#notifyElementEvicted(net.sf.ehcache.Ehcache,
		 * net.sf.ehcache.Element)
		 */
		public void notifyElementEvicted(final Ehcache cache, final Element elt) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Closing factory associated to key (eviction): " + elt.getKey());
			}

			close(elt);
		}

		/*
		 * (non-Javadoc)
		 * @see net.sf.ehcache.event.CacheEventListener#dispose()
		 */
		public void dispose() {}
	}

	/**
	 * Instantiates a new cache.
	 */
	public EntityManagerFactoryCache() {
		this(CONFIG_FILE);
	}

	/**
	 * Instantiates a new cache.
	 * @param configurationFile the configuration file
	 */
	public EntityManagerFactoryCache(final String configurationFile) {
		super();

		InputStream in = null;

		LOGGER.debug("Loading " + CACHE_NAME + " from configuration file " + configurationFile);

		try {
			in = EntityManagerFactoryCache.class.getClassLoader().getResourceAsStream(configurationFile);
			initializeDelegate(in, CACHE_NAME);
			getDelegate().getCacheEventNotificationService().registerListener(
			    new CleanupCacheEventListener());
		}
		finally {
			if (in != null) {
				try {
					in.close();
				}
				catch (final Exception e) {
					LOGGER.debug("Failed to close stream on configuration file: " + configurationFile, e);
				}
			}
		}
	}
}
